SQSトリガーで起動するLambdaは非同期呼び出しではないので、DLQの設定はSQSでやろう。LambdaのDLQやDestinationはダメでした。
SNS→SQS→Lambdaの組み合わせを使っている方は多いと思います。 このとき、LambdaがErrorになってもSQSにデータが残り続けるため、任意時間後に自動で再実行してくれます。便利ですね。
しかし、何らかの理由によってDLQを設定するとき、LambdaのDLQやDestinationを設定しても、動作しませんでした。 理由はSQSトリガーで動作するLambdaは非同期呼び出しではないからです。DLQを使いたい場合は、SQSのDLQを使いましょう。
実際に試してみました。
おすすめの方
- SQSトリガーで起動するLambdaをAWS SAMで作りたい方
- SQSにDLQを設定したい方
- SNS -> SQSの構成を作りたい方
まずは、LambdaのDLQやDestinationが動作しないことを確認する
sam init
sam init \ --runtime python3.8 \ --name Lambda-Sqs-Dlq-Test-Sample \ --app-template hello-world \ --package-type Zip
SAMテンプレート
下記を作成しています。
- SNS
- SQS
- SNS→SQSのポリシー
- Lambda
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Lambda-Sqs-Dlq-Test-Sample Resources: # LambdaのDLQ用のSNSトピック HelloWorldFunctionDlqTopic: Type: AWS::SNS::Topic Properties: Subscription: - Protocol: email Endpoint: sample@example.com SampleTopic: Type: AWS::SNS::Topic Properties: Subscription: - Protocol: sqs Endpoint: !GetAtt SampleQueue.Arn SampleQueue: Type: AWS::SQS::Queue Properties: VisibilityTimeout: 120 QueuePolicy: Type: AWS::SQS::QueuePolicy Properties: Queues: - !Ref SampleQueue PolicyDocument: Statement: - Action: - sqs:SendMessage Effect: Allow Resource: "*" Principal: Service: sns.amazonaws.com HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.8 Timeout: 5 DeadLetterQueue: Type: SNS TargetArn: !Ref HelloWorldFunctionDlqTopic Policies: - arn:aws:iam::aws:policy/AmazonSNSFullAccess EventInvokeConfig: MaximumEventAgeInSeconds: 60 MaximumRetryAttempts: 1 DestinationConfig: OnFailure: Type: SNS Destination: !Ref HelloWorldFunctionDlqTopic Events: SQS: Type: SQS Properties: BatchSize: 5 Queue: !GetAtt SampleQueue.Arn HelloWorldFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}
Lambdaコード
強制的にErrorを発生させています。
def lambda_handler(event, context): raise NotImplementedError()
デプロイ
sam build sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-deploy sam deploy \ --template-file packaged.yaml \ --stack-name Lambda-Sqs-Dlq-Tests-Sample-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
SNSにメッセージを発行して、DLQの動きを確認する
SNSにメッセージを発行します。
しばらく待っても、DLQ用のSNSトピックを経由したメールは届きません。
なお、CloudWatch LogsでLambdaのログを確認すると、VisibilityTimeout:120
ごとにLambdaが実行されています。
SQSは、「処理中のメッセージ」にキューが溜まっています。
SQSのDLQを設定する
SAMテンプレート
下記を追加します。
- DLQ用のSQS
- DLQ用のSQSを試しに購読するLambda(ログを出すだけ)
- DLQ用のSQSトリガーで起動し、eventをログ出力するLambda
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Lambda-Sqs-Dlq-Test-Sample Resources: # LambdaのDLQ用のSNSトピック HelloWorldFunctionDlqTopic: Type: AWS::SNS::Topic Properties: Subscription: - Protocol: email Endpoint: sample@example.com SampleTopic: Type: AWS::SNS::Topic Properties: Subscription: - Protocol: sqs Endpoint: !GetAtt SampleQueue.Arn SampleQueue: Type: AWS::SQS::Queue Properties: VisibilityTimeout: 120 RedrivePolicy: maxReceiveCount: 2 deadLetterTargetArn: !GetAtt SampleDeadLatterQueue.Arn # DLQ用のSQS SampleDeadLatterQueue: Type: AWS::SQS::Queue Properties: VisibilityTimeout: 60 QueuePolicy: Type: AWS::SQS::QueuePolicy Properties: Queues: - !Ref SampleQueue PolicyDocument: Statement: - Action: - sqs:SendMessage Effect: Allow Resource: "*" Principal: Service: sns.amazonaws.com HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.8 Timeout: 5 DeadLetterQueue: Type: SNS TargetArn: !Ref HelloWorldFunctionDlqTopic Policies: - arn:aws:iam::aws:policy/AmazonSNSFullAccess EventInvokeConfig: MaximumEventAgeInSeconds: 60 MaximumRetryAttempts: 1 DestinationConfig: OnFailure: Type: SNS Destination: !Ref HelloWorldFunctionDlqTopic Events: SQS: Type: SQS Properties: BatchSize: 5 Queue: !GetAtt SampleQueue.Arn HelloWorldFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${HelloWorldFunction} DumpSnsFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app2.lambda_handler Runtime: python3.8 Timeout: 5 Events: SQS: Type: SNS Properties: Topic: !Ref HelloWorldFunctionDlqTopic DumpSnsFunctionLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${DumpSnsFunction}
DLQ用のSQSを試しに購読するLambda
event
をログ出力します。
def lambda_handler(event, context): print(event)
デプロイ
sam build sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-deploy sam deploy \ --template-file packaged.yaml \ --stack-name Lambda-Sqs-Dlq-Tests-Sample-Stack \ --s3-bucket cm-fujii.genki-deploy \ --capabilities CAPABILITY_NAMED_IAM \ --no-fail-on-empty-changeset
DLQ用のSQSを試しに購読するLambdaのログ
「SNS -> SQS -> Lambda」でLambdaがErrorになり続けていましたが、SQSに設定したDLQ(SQS)にキューが移動します。このDLQ(SQS)をトリガーに動くLambdaのログを見ると、「This is a pen.」を受け取れています。
「SNS -> SQS -> Lambda」にあったSQSのキューも処理されています。
さいごに
SQSトリガーで動作するLambdaは、非同期呼び出しではありません。DLQを使いたい場合は、SQSのDLQを使いましょう。
これに気づくまで半日以上かかりました……。どなたかの参考になれば幸いです。